/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <set>
#include <cinttypes>
#include "wifi_common_util.h"
#include "wifi_lock_manager.h"
#include "wifi_logger.h"
#include "wifi_supplicant_hal_interface.h"
#include "wifi_service_manager.h"
#include "wifi_settings.h"
#include "system_ability_definition.h"
#include "connection_observer_client.h"
#include "app_mgr_client.h"
#include "app_mgr_interface.h"
#include "app_process_data.h"
#include "iservice_registry.h"

DEFINE_WIFILOG_LABEL("WifiLockManager");

namespace OHOS {
namespace Wifi {
constexpr const int WIFI_LOCK_APP_MAX = 100;
WifiLockManagerBase::WifiLockManagerBase()
{

}

WifiLockManagerBase::~WifiLockManagerBase()
{

}

ErrCode WifiLockManagerBase::AcquireWifiLock(pid_t callingUid)
{
    TimeStats timeStats(__func__);
    WIFI_LOGI("Enter AcquireWifiLock, callingUid %{public}d.", callingUid);
    {
        std::set<pid_t>::iterator iter;
        std::unique_lock<std::mutex> lock(mutex_);
        iter = wifiLockBundles_.find(callingUid);
        if (iter != wifiLockBundles_.end()) {
            WIFI_LOGE("%{public}s bundle %{public}d has acquired wifi lock.", __func__, callingUid);
            return WIFI_OPT_FAILED;
        } else {
            if (refCount_ >= WIFI_LOCK_APP_MAX) {
                WIFI_LOGE("%{public}s Exceeded maximum number of wifi locks[%{public}d].", __func__, WIFI_LOCK_APP_MAX);
                return WIFI_OPT_FAILED;
            }
            wifiLockBundles_.insert(callingUid);
            refCount_ += 1;
        }
    }
    if (!UpdateWifiMode()) {
        WIFI_LOGE("%{public}s UpdateWifiMode failed.", __func__);
        return WIFI_OPT_FAILED;
    }
    WIFI_LOGD("%{public}s app [uid: %{public}d] acquird Wifi lock successful.", \
         __func__, callingUid);

    return WIFI_OPT_SUCCESS;
}

ErrCode WifiLockManagerBase::ReleaseWifiLock(pid_t callingUid)
{
    TimeStats timeStats(__func__);
    WIFI_LOGI("Enter ReleaseWifiLock, callingUid %{public}d.", callingUid);
    {
        std::set<pid_t>::iterator iter;
        std::unique_lock<std::mutex> lock(mutex_);
        iter = wifiLockBundles_.find(callingUid);
        if (iter != wifiLockBundles_.end()) {
            wifiLockBundles_.erase(iter);
            refCount_ -= 1;
            
        } else {
            WIFI_LOGE("%{public}s bundle %{public}d wifi lock has been released or not acquired wifi lock yet.", \
                __func__, callingUid);
            return WIFI_OPT_FAILED;
        }
    }

    if (!UpdateWifiMode()) {
        WIFI_LOGE("UpdateWifiMode failed.");
        return WIFI_OPT_FAILED;
    }
    WIFI_LOGD("%{public}s app [uid: %{public}d] release Wifi lock successful.", __func__, callingUid);

    return WIFI_OPT_SUCCESS;
}

bool WifiLockManagerBase::UpdateWifiMode()
{
    IStaService *pService = WifiServiceManager::GetInstance().GetStaServiceInst();
    if (pService == nullptr) {
        WIFI_LOGE("%{public}s sta service is NOT start!", __func__);
        return false;
    }

    std::unique_lock<std::mutex> lock(mutex_);
    WifiMode newMode = GetLockMode();
    WIFI_LOGI("%{public}s currentMode %{public}d, newMode %{public}d ", \
        __func__, static_cast<int>(currentWifiMode_), static_cast<int>(newMode));
    if (newMode == currentWifiMode_) {
        WIFI_LOGI("Wifi has been in newMode[%{public}d].", static_cast<int>(newMode));
        return true;
    }

    ResetCurrentMode();

    return SetNewMode(newMode);
}

ConnState WifiLockManagerBase::GetWifiConnectState()
{
    WifiLinkedInfo info;
    int ret = WifiSettings::GetInstance().GetLinkedInfo(info);
    if (ret != 0) {
        WIFI_LOGE("Get wifi linked info failed.");
        return UNKNOWN;
    }
    return info.connState; 
}

WifiMode WifiLockManagerBase::GetLockMode() 
{
    return WIFI_MODE_NO_LOCKS_HELD;
}

bool WifiLockManagerBase::SetNewMode(WifiMode newLockMode)
{
    return true;
}

bool WifiLockManagerBase::ResetCurrentMode()
{
    return true;
}

void WifiLockManagerBase::RemoveLockForAppDie(pid_t uid)
{
    WIFI_LOGI("Enter %{public}s, remove app uid %{public}d.", __func__, uid);
    std::set<pid_t>::iterator iter;
    std::unique_lock<std::mutex> lock(mutex_);
    iter = wifiLockBundles_.find(uid);
    if (iter != wifiLockBundles_.end()) {
        wifiLockBundles_.erase(iter);
        refCount_ -= 1;
        WIFI_LOGD("App %{public}d was removed, wifiLockBundles_.size %{public}d refCount %{public}d", \
            uid, wifiLockBundles_.size(), refCount_);
    } else {
        WIFI_LOGI("App %{public}d not in wifiLockBundles_, wifiLockBundles_.size %{public}d refCount %{public}d", \
            uid, wifiLockBundles_.size(), refCount_);
    }
}

void AppStateObserver::OnAppStarted(const AppExecFwk::AppStateData &appStateData)
{

}

void AppStateObserver::OnAppStopped(const AppExecFwk::AppStateData &appStateData)
{
    if (appStateData.bundleName.empty()) {
        WIFI_LOGE("App bundle name is empty");
        return;
    }

    WifiLockManager::GetInstance()->RemoveLockForAppDie(appStateData.uid);
    WifiLockManager::GetInstance()->UpdateWifiMode();
    return;
}

void AppStateObserver::OnProcessCreated(const AppExecFwk::ProcessData &processData)
{

}

void AppStateObserver::OnProcessDied(const AppExecFwk::ProcessData &processData)
{

}
}
}
